﻿/* 
 *  Tutorials:
 * 
 *  Text im 3D-Raum
 *  - http://xboxforums.create.msdn.com/forums/p/17254/90030.aspx
 * 
 *  Bloom
 *  - http://xbox.create.msdn.com/en-US/education/catalog/sample/bloom
 *  - http://www.klopfenstein.net/lorenz.aspx/gamecomponents-the-bloom-post-processing-filter
 * 
 *  Rendering Bounding Spheres
 *  - http://www.xnawiki.com/index.php?title=Rendering_Bounding_Spheres
 *  
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace gdv
{
    public class DietzeAndreasPlanet : Microsoft.Xna.Framework.DrawableGameComponent
    {
        public Matrix world, view, projection;
        SpriteBatch spriteBatch;
        Model sunFBX, earthFBX, moonFBX;
        Texture2D sunTexture, earthTexture;
        float sunRotY;
        float moonRot;

        public Effect glowEffect;
        DietzeAndreasBloomComponent bloom;

        SpriteFont planetFont;
        Vector3 screenspace;
        Vector2 textposition;
        Vector2 stringCenter;

        //Cam
        public Vector3 camPosition = new Vector3(0, 0, 500);
        public Vector3 lookAt = new Vector3(0, 0, 0);
        public Vector3 up = Vector3.Up;
        public Quaternion camRotation = Quaternion.Identity;
        private float leftrightRot;
        private float updownRot;
        private float rollRot;
        private float rotationSpeed = 0.005f;
        private float sensibility = 5.5f;
        private float moveSpeed = 1f;

        int width;
        int height;
        private MouseState originalMouseState;

        KeyboardState kbState, lastkbState;

        enum RotationState { pos, neg };
        private RotationState rotState = RotationState.pos;

        public DietzeAndreasPlanet(Game game)
            : base(game)
        {
            sunRotY = 0.0f;
            moonRot = 0.0f;
        }

        public override void Initialize()
        {
            world = Matrix.Identity;
            projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, 16.0f / 9.0f, 0.1f, 100000.0f);

            lastkbState = Keyboard.GetState();

            bloom = new DietzeAndreasBloomComponent(this.Game);
            Game.Components.Add(bloom);

            base.Initialize();
        }

        protected override void LoadContent()
        {
            this.spriteBatch = new SpriteBatch(GraphicsDevice);

            width = GraphicsDevice.PresentationParameters.BackBufferWidth;
            height = GraphicsDevice.PresentationParameters.BackBufferHeight;

            Mouse.SetPosition(width / 2, height / 2);
            originalMouseState = Mouse.GetState();

            // Models
            sunFBX = Game.Content.Load<Model>(@"DietzeAndreasAssets/DietzeAndreasSun");
            sunTexture = Game.Content.Load<Texture2D>(@"DietzeAndreasAssets/SunLayer");
            earthFBX = Game.Content.Load<Model>(@"DietzeAndreasAssets/DietzeAndreasEarth1");
            moonFBX = Game.Content.Load<Model>(@"DietzeAndreasAssets/DietzeAndreasMoon");

            // Textures
            earthTexture = Game.Content.Load<Texture2D>(@"DietzeAndreasAssets/EarthMap");

            // Effects
            glowEffect = Game.Content.Load<Effect>(@"DietzeAndreasAssets/DietzeAndreasGlow");

            // Fonts
            planetFont = Game.Content.Load<SpriteFont>(@"DietzeAndreasAssets/DietzeAndreasPlanetFont");

            base.LoadContent();
        }

        public override void Update(GameTime gameTime)
        {
            kbState = Keyboard.GetState();

            if (Keyboard.GetState().IsKeyDown(Keys.Escape))
                this.Game.Exit();

            if ((kbState.IsKeyDown(Keys.X) && lastkbState.IsKeyUp(Keys.X)) && rotState == RotationState.neg)
            {
                rotState = RotationState.pos;
            }
            else if ((kbState.IsKeyDown(Keys.X) && lastkbState.IsKeyUp(Keys.X)) && rotState == RotationState.pos)
            {
                rotState = RotationState.neg;
            }

            lastkbState = Keyboard.GetState();

            if (rotState == RotationState.pos)
            {
                sunRotY += 0.2f;
                moonRot += 0.4f;
            }

            if (rotState == RotationState.neg)
            {
                sunRotY -= 0.2f;
                moonRot -= 0.4f;
            }

            if ((sunRotY % 360) == 0.0f)
                sunRotY = 0.0f;

            if ((moonRot % 360) == 0.0f)
                moonRot = 0.0f;

            UpdateCameraFirstPerson();

            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            bloom.BeginDraw();
            GraphicsDevice.Clear(Color.Black);
            GraphicsDevice.DepthStencilState = DepthStencilState.Default;

            DrawSunWithBloom();
            DrawEarth();
            DrawMoon();

            base.Draw(gameTime);

            GraphicsDevice.DepthStencilState = DepthStencilState.None;
            DrawSunName();
            DrawEarthName();
            DrawMoonName();
        }

        private void DrawSunWithBasicShader()
        {
            Matrix translation = Matrix.CreateScale(1.0f, 1.0f, 1.0f) * Matrix.CreateRotationY(MathHelper.ToRadians(sunRotY));

            foreach (ModelMesh mesh in sunFBX.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.World = world * translation;
                    effect.View = view;
                    effect.Projection = projection;
                }
                mesh.Draw();
            }
        }

        private void DrawSunWithBloom()
        {
            Matrix translation = Matrix.CreateScale(100.0f, 100.0f, 100.0f) * Matrix.CreateRotationY(MathHelper.ToRadians(sunRotY / 6));

            foreach (ModelMesh mesh in sunFBX.Meshes)
            {
                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    part.Effect = glowEffect;
                    glowEffect.Parameters["World"].SetValue(world * mesh.ParentBone.Transform * translation);
                    glowEffect.Parameters["View"].SetValue(view);
                    glowEffect.Parameters["Projection"].SetValue(projection);
                    glowEffect.Parameters["Texture"].SetValue(sunTexture);
                }
                mesh.Draw();
            }
        }

        public void DrawEarth()
        {
            Matrix translation = Matrix.CreateScale(10.0f, 10.0f, 10.0f)
                               * Matrix.CreateRotationX(MathHelper.ToRadians(-90.0f))
                               * Matrix.CreateRotationY(MathHelper.ToRadians(sunRotY))
                               * Matrix.CreateTranslation(new Vector3(-1500.0f, 0.0f, 0.0f))
                               * Matrix.CreateRotationY(MathHelper.ToRadians(sunRotY / 40));

            Vector3 earthPos = Vector3.Transform(Vector3.Zero, translation);

            foreach (ModelMesh mesh in earthFBX.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.LightingEnabled = true;
                    effect.DirectionalLight0.Enabled = true;
                    effect.DirectionalLight0.Direction = earthPos;
                    effect.EmissiveColor = new Vector3(0.01f, 0.01f, 0.01f);
                    effect.World = world * translation;
                    effect.View = view;
                    effect.Projection = projection;
                }
                mesh.Draw();
            }

            //Earth-Orbit
            DiezteAndreasOrbit.Render(new Vector3(1500.0f, 0.0f, 1500.0f), Vector3.Zero, GraphicsDevice, view, projection, Color.White);
            //Moon-Orbit
            DiezteAndreasOrbit.Render(new Vector3(40.0f, 0.0f, 40.0f), earthPos, GraphicsDevice, view, projection, Color.White);
        }

        public void DrawMoon()
        {
            Matrix translation = Matrix.CreateScale(2.5f, 2.5f, 2.5f)
                               * Matrix.CreateTranslation(new Vector3(((float)Math.Sin(MathHelper.ToRadians(moonRot)) * 40), 0.0f, ((float)Math.Cos(MathHelper.ToRadians(moonRot)) * 40)))
                               * Matrix.CreateTranslation(new Vector3(-1500.0f, 0.0f, 0.0f))
                               * Matrix.CreateRotationY(MathHelper.ToRadians(sunRotY / 40));

            Vector3 moonPos = Vector3.Transform(Vector3.Zero, translation);

            foreach (ModelMesh mesh in moonFBX.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.LightingEnabled = true;
                    effect.DirectionalLight0.Enabled = true;
                    effect.DirectionalLight0.Direction = moonPos;
                    effect.World = world * translation;
                    effect.View = view;
                    effect.Projection = projection;
                }
                mesh.Draw();
            }
        }

        public void DrawSunName()
        {
            screenspace = GraphicsDevice.Viewport.Project(Vector3.Zero, projection, view, Matrix.CreateTranslation(new Vector3(0.0f, 120.0f, 0.0f)));

            textposition.X = screenspace.X;
            textposition.Y = screenspace.Y;

            stringCenter = planetFont.MeasureString("Sun") * 0.5f;

            textposition.X = (int)(textposition.X - stringCenter.X);
            textposition.Y = (int)(textposition.Y - stringCenter.Y);

            if (screenspace.Z < 1.0f) // The gamer is in front so draw his name. 
            {
                spriteBatch.Begin();
                spriteBatch.DrawString(planetFont, "Sun", textposition, Color.Orange);
                spriteBatch.End();
            }
        }

        public void DrawEarthName()
        {
            screenspace = GraphicsDevice.Viewport.Project(Vector3.Zero, projection, view,
                Matrix.CreateTranslation(new Vector3(0.0f, 0.0f, 15.0f)) *
                Matrix.CreateRotationX(MathHelper.ToRadians(-90.0f)) *
                Matrix.CreateTranslation(new Vector3(-1500.0f, 0.0f, 0.0f)) *
                Matrix.CreateRotationY(MathHelper.ToRadians(sunRotY / 40)));

            textposition.X = screenspace.X;
            textposition.Y = screenspace.Y;

            stringCenter = planetFont.MeasureString("Earth") * 0.5f;

            textposition.X = (int)(textposition.X - stringCenter.X);
            textposition.Y = (int)(textposition.Y - stringCenter.Y);

            if (screenspace.Z < 1.0f)
            {
                spriteBatch.Begin();
                spriteBatch.DrawString(planetFont, "Earth", textposition, Color.Orange);
                spriteBatch.End();
            }
        }

        public void DrawMoonName()
        {
            screenspace = GraphicsDevice.Viewport.Project(Vector3.Zero, projection, view, Matrix.CreateTranslation(new Vector3(0.0f, 5.0f, 0.0f)) *
                Matrix.CreateTranslation(new Vector3(((float)Math.Sin(MathHelper.ToRadians(moonRot)) * 40), 0.0f, ((float)Math.Cos(MathHelper.ToRadians(moonRot)) * 40))) *
                Matrix.CreateTranslation(new Vector3(-1500.0f, 0.0f, 0.0f)) *
                 Matrix.CreateRotationY(MathHelper.ToRadians(sunRotY / 40)));

            textposition.X = screenspace.X;
            textposition.Y = screenspace.Y;

            stringCenter = planetFont.MeasureString("Moon") * 0.5f;

            textposition.X = (int)(textposition.X - stringCenter.X);
            textposition.Y = (int)(textposition.Y - stringCenter.Y);

            if (screenspace.Z < 1.0f)
            {
                spriteBatch.Begin();
                spriteBatch.DrawString(planetFont, "Moon", textposition, Color.Orange);
                spriteBatch.End();
            }
        }

        private void UpdateCameraFirstPerson()
        {
            MouseState currentMouseState = Mouse.GetState();
            KeyboardState keyState = Keyboard.GetState();

            if (currentMouseState != originalMouseState)
            {
                float xDifference = currentMouseState.X - originalMouseState.X;
                float yDifference = currentMouseState.Y - originalMouseState.Y;
                leftrightRot -= rotationSpeed * xDifference;
                updownRot -= rotationSpeed * yDifference;
                Mouse.SetPosition(width / 2, height / 2); //Nur wenn maus sich bewegt, ansonsten konstante bewegung !
                currentMouseState = originalMouseState;
            }

            if (keyState.IsKeyDown(Keys.Y))
            {
                rollRot += rotationSpeed;
            }

            if (keyState.IsKeyDown(Keys.C))
            {
                rollRot -= rotationSpeed;
            }

            if (keyState.IsKeyDown(Keys.Up) || keyState.IsKeyDown(Keys.W))      //Forward
            {
                AddToCameraPosition(new Vector3(0, 0, -sensibility));

            }

            if (keyState.IsKeyDown(Keys.Down) || keyState.IsKeyDown(Keys.S))    //Backward
            {
                AddToCameraPosition(new Vector3(0, 0, sensibility));

            }

            if (keyState.IsKeyDown(Keys.Right) || keyState.IsKeyDown(Keys.D))   //Right
            {
                AddToCameraPosition(new Vector3(sensibility, 0, 0));

            }

            if (keyState.IsKeyDown(Keys.Left) || keyState.IsKeyDown(Keys.A))    //Left
            {
                AddToCameraPosition(new Vector3(-sensibility, 0, 0));

            }

            if (keyState.IsKeyDown(Keys.E))                                     //Up
            {
                AddToCameraPosition(new Vector3(0, sensibility, 0));

            }
            if (keyState.IsKeyDown(Keys.Q))                                     //Down
            {
                AddToCameraPosition(new Vector3(0, -sensibility, 0));

            }

            UpdateViewMatrix();
        }

        private void UpdateViewMatrix()
        {
            Matrix cameraRotation = Matrix.CreateRotationX(updownRot) * Matrix.CreateRotationY(leftrightRot) * Matrix.CreateRotationZ(rollRot);
            Vector3 cameraOriginalTarget = new Vector3(0, 0, -1);
            Vector3 cameraOriginalUpVector = new Vector3(0, 1, 0);
            Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation);
            lookAt = camPosition + cameraRotatedTarget;
            up = Vector3.Transform(cameraOriginalUpVector, cameraRotation);
            view = Matrix.CreateLookAt(camPosition, lookAt, up);
        }

        private void AddToCameraPosition(Vector3 vectorToAdd)
        {
            Matrix cameraRotation = Matrix.CreateRotationX(updownRot) * Matrix.CreateRotationY(leftrightRot) * Matrix.CreateRotationZ(rollRot);
            Vector3 rotatedVector = Vector3.Transform(vectorToAdd, cameraRotation);
            camPosition += moveSpeed * rotatedVector;
            UpdateViewMatrix();
        }
    }
}



